package com.tanhua.server.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.PageUtil;
import com.github.tobato.fastdfs.domain.conn.FdfsWebServer;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.tanhua.autoconfig.template.OssTemplate;
import com.tanhua.commons.utils.Constants;
import com.tanhua.dubbo.api.CommentApi;
import com.tanhua.dubbo.api.FocusUserApi;
import com.tanhua.dubbo.api.UserInfoApi;
import com.tanhua.dubbo.api.VideoApi;
import com.tanhua.model.domain.UserInfo;
import com.tanhua.model.enums.CommentType;
import com.tanhua.model.mongo.Comment;
import com.tanhua.model.mongo.FocusUser;
import com.tanhua.model.mongo.Video;
import com.tanhua.model.vo.CommentVo;
import com.tanhua.model.vo.ErrorResult;
import com.tanhua.model.vo.PageResult;
import com.tanhua.model.vo.VideoVo;
import com.tanhua.server.exception.BusinessException;
import com.tanhua.server.interceptor.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
public class SmallVideoService {

    @Autowired
    private FastFileStorageClient client;

    @Autowired
    private FdfsWebServer fdfsWebServer;

    @DubboReference
    private VideoApi videoApi;

    @Autowired
    private OssTemplate ossTemplate;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @DubboReference
    private UserInfoApi userInfoApi;

    @DubboReference
    private FocusUserApi focusUserApi;

    @Autowired
    private MqMessageService mqMessageService;

    /**
     * 视频上传
     *
     * @param videoThumbnail 封面图片
     * @param videoFile      视频地址
     */
    public void saveVideos(MultipartFile videoThumbnail, MultipartFile videoFile) throws IOException {
        //1.使用fdfs上传视频
        //文件的后缀名 xx.xxx
        String filename = videoFile.getOriginalFilename();
        //通过最后一个.进行切割获取后缀名
        String substring = filename.substring(filename.lastIndexOf(".") + 1);
        //文件输入流
        InputStream inputStream = videoFile.getInputStream();
        //四个参数 文件的输入流，文件的长度，文件的类型，文件的元数据
        StorePath path = client.uploadFile(inputStream, videoFile.getSize(), substring, null);
        //获取地址
        String url = fdfsWebServer.getWebServerUrl() + path.getFullPath();
        //2.使用阿里云上传封面图片，获取地址
        String upload = ossTemplate.upload(videoThumbnail.getOriginalFilename(), videoThumbnail.getInputStream());
        //3.组装对象设置属性存到数据库
        Video video = new Video();
        video.setUserId(UserHolder.getUserId());
        video.setPicUrl(upload);
        video.setVideoUrl(url);
        video.setText("你今天玩游戏了吗？");
        //4.调用api保存数据
        String videoId = videoApi.sava(video);
        //视频上传日志
        mqMessageService.sendLogService(UserHolder.getUserId(), "0301", "movement", videoId);
        //5.如果保存成功会自动生成一个id，判断是否存在
        if (StringUtils.isEmpty(videoId)) {
            //不存在抱一个异常
            throw new BusinessException(ErrorResult.error());
        }
    }

    /**
     * 视频列表
     *
     * @param page
     * @param pagesize
     * @return
     */
    //redis中key规则：userid_page_pagesize
    //T(类路径)表示引用java中的类
    //#表示引用方法列表上的某个参数
    //最终完整key   video::userid_page_pagesize
    @Cacheable(value = "video",
            key = "T(com.tanhua.server.interceptor.UserHolder).getUserId()+'_'+#page+'_'+#pagesize")
    public PageResult queryVideoList(Integer page, Integer pagesize) {
        //1.先从redis中查询视频列表
        String redisKey = Constants.VIDEOS_RECOMMEND + UserHolder.getUserId();
        String redisValue = redisTemplate.opsForValue().get(redisKey);
        //2.判断数据是否为空
        List<Video> list = new ArrayList<>();
        int totalPage = 0;
        if (!StringUtils.isEmpty(redisValue)) {
            //3.不为空时
            //获取的数据通过,切割
            String[] split = redisValue.split(",");
            //4.通过stream流把切割好的数据存到集合中
            if ((page - 1) * pagesize < split.length) {
                List<Long> vids = Arrays.stream(split).skip((page - 1) * pagesize)
                        .limit(pagesize)
                        .map(e -> Long.valueOf(e))
                        .collect(Collectors.toList());
                //调用api根据vid查询数据
                list = videoApi.queryVideoList(vids);
            }
            //调用工具类获取当前页数
            totalPage = PageUtil.totalPage(split.length, pagesize);
        }
        //6.判断集合是否为空，如果从redis中查询到数据肯定就不为空了
        if (CollUtil.isEmpty(list)) {
            //为空，没有查到数据 就分页查询视频数据
            //page的计算规则，  传入的页码  - redis查询的总页数
            list = videoApi.pr(page - totalPage, pagesize);
        }
        //7.封装vo返回数据
        //获取到每一个用户id详情
        List<Long> userIds = CollUtil.getFieldValues(list, "userId", Long.class);
        Map<Long, UserInfo> infoMap = userInfoApi.findByIds(userIds, null);
        List<VideoVo> vos = new ArrayList<>();
        for (Video video : list) {
            UserInfo userInfo = infoMap.get(video.getUserId());
            if (userInfo != null) {
                VideoVo vo = VideoVo.init(userInfo, video);
                //从redis中获取标记key，查看当前用户是否关注了视频发布作者
                String key = Constants.FOCUS_USER_KEY + UserHolder.getUserId();
                String hashkey = String.valueOf(video.getUserId());
                if (redisTemplate.opsForHash().hasKey(key, hashkey)) {
                    vo.setHasFocus(1);
                }
                //从redis中获取标记key，查看当前用户是否点赞了视频作者
                String likeKey = Constants.VIDEO_LIKE_HASHKEY + video.getId();
                Object hashLikeKey = Constants.VIDEO_LIKE_HASHKEY + UserHolder.getUserId();
                if (redisTemplate.opsForHash().hasKey(likeKey, hashLikeKey)) {
                    vo.setHasLiked(1);
                }
                vos.add(vo);
            }
        }
        return new PageResult(page, pagesize, 0L, vos);
    }

    /**
     * 关注视频
     *
     * @param followUserId
     */
    public void userFocus(Long followUserId) {
        //1.FocusUser，设置属性
        FocusUser focusUser = new FocusUser();
        focusUser.setUserId(UserHolder.getUserId());
        focusUser.setFollowUserId(followUserId);
        //2.调用api保存数据
        focusUserApi.save(focusUser);
        //3.存储关注标记到redis中
        String key = Constants.FOCUS_USER_KEY + UserHolder.getUserId();
        String hashkey = String.valueOf(followUserId);
        redisTemplate.opsForHash().put(key, hashkey, "1");
    }

    /**
     * 取消视频关注
     *
     * @param followUserId
     */
    public void userUnFocus(Long followUserId) {
        //1.调用api删除数据
        focusUserApi.delete(UserHolder.getUserId(), followUserId);
        //2.删除redis中关注的标记
        String key = Constants.FOCUS_USER_KEY + UserHolder.getUserId();
        String hashkey = String.valueOf(followUserId);
        redisTemplate.opsForHash().delete(key, hashkey, "1");
    }


    @DubboReference
    private CommentApi commentApi;

    /**
     * 视频点赞
     *
     * @param videoId 视频id
     */
    public void like(String videoId) {
        //1.调用api查询是否已经点赞
        boolean flag = commentApi.like(videoId, UserHolder.getUserId(), CommentType.LIKE);
        //2.如果已经点赞 抛出异常
        if (flag) {
            throw new BusinessException(ErrorResult.error());
        }
        //3.没有点赞，保存数据到互动表
        Comment comment = new Comment();
        comment.setCommentType(CommentType.LIKE.getType());
        comment.setUserId(UserHolder.getUserId());
        comment.setPublishId(new ObjectId(videoId.toString()));
        comment.setCreated(System.currentTimeMillis());
        commentApi.saveVideo(comment);
        //4.保存点赞的标记到redis中
        String key = Constants.VIDEO_LIKE_HASHKEY + videoId;
        Object hashKey = Constants.VIDEO_LIKE_HASHKEY + UserHolder.getUserId();
        redisTemplate.opsForHash().put(key, hashKey, "1");
    }

    /**
     * 取消视频点赞
     *
     * @param videoId
     */
    public void dislike(String videoId) {
        //1.判断视频是否已经点赞
        boolean flag = commentApi.like(videoId, UserHolder.getUserId(), CommentType.LIKE);
        //2.如果没有点赞抛出异常
        if (!flag) {
            throw new BusinessException(ErrorResult.error());
        }
        //3.删除数据库中的记录
        Comment comment = new Comment();
        comment.setCommentType(CommentType.LIKE.getType());
        comment.setUserId(UserHolder.getUserId());
        comment.setPublishId(new ObjectId(videoId));
        commentApi.remove(comment);
        //4.删除redis中点赞的标识
        String key = Constants.VIDEO_LIKE_HASHKEY + videoId;
        Object hashKey = Constants.VIDEO_LIKE_HASHKEY + UserHolder.getUserId();
        redisTemplate.opsForHash().delete(key, hashKey);
    }

    /**
     * 发布评论
     *
     * @param videoId 视频id
     * @param ccc 评论内容
     */
    public void comments(String videoId, String ccc) {
        //1.构造Comment
        Comment ct = new Comment();
        ct.setCommentType(CommentType.COMMENT.getType());
        ct.setCreated(System.currentTimeMillis());
        ct.setContent(ccc);
        ct.setUserId(UserHolder.getUserId());
        ct.setPublishId(new ObjectId(videoId));
        //2.调用方法保存数据
        commentApi.saveVideo(ct);
    }

   /**
     * 视频评论列表
     * @param videoId
     * @param page
     * @param pagesize
     * @return
     */
    public PageResult page(String videoId, Integer page, Integer pagesize) {
        //1.调用api查询互动数据
        List<Comment> comments = commentApi.findComments(videoId, CommentType.COMMENT, page, pagesize);
        if (CollUtil.isEmpty(comments)) {
            return new PageResult();
        }
        //2.获取互动数据表里每一个评论人的id
        List<Long> userIds = CollUtil.getFieldValues(comments, "userId", Long.class);
        //3.调用api获取每个人的用户详情
        Map<Long, UserInfo> map = userInfoApi.findByIds(userIds, null);
        //4.封装vo返回
        List<CommentVo> vos = new ArrayList<>();
        for (Comment comment : comments) {
            UserInfo userInfo = map.get(comment.getUserId());
            CommentVo vo = CommentVo.init(userInfo, comment);
            vos.add(vo);
        }
        return new PageResult(page, pagesize, 0L, vos);
    }
}
